using System;
using System.IO;



namespace Framework.Network
{
    public enum ParserState
    {
        PacketSize,
        PacketBody,
    }

    public static class Packet
    {
        public const int PacketSizeLength2 = 2;
        public const int PacketSizeLength4 = 4;
        public const int MinPacketSize = 2;
        public const int OpcodeIndex = 0;
        public const int MessageIndex = 2;
    }

    public class PacketParser
    {
        private readonly CircularBuffer m_Buffer;
        private int m_PacketSize;
        private ParserState m_State;
        private bool m_IsOK;
        private readonly int m_PacketSizeLength;

        public MemoryStream MemoryStream { get; }

        public PacketParser(int packetSizeLength, CircularBuffer buffer, MemoryStream memoryStream)
        {
            this.m_PacketSizeLength = packetSizeLength;
            this.m_Buffer = buffer;
            this.MemoryStream = memoryStream;
        }

        public bool Parse()
        {
            if (this.m_IsOK)
            {
                return true;
            }
            bool finish = false;
            while (!finish)
            {
                switch (this.m_State)
                {
                    case ParserState.PacketSize:
                    {
                        if (this.m_Buffer.Length < this.m_PacketSizeLength)
                        {
                            finish = true;
                        }
                        else
                        {
                            this.m_Buffer.Read(MemoryStream.GetBuffer(), 0, this.m_PacketSizeLength);
                            switch (this.m_PacketSizeLength)
                            {
                                case Packet.PacketSizeLength4:
                                    this.m_PacketSize = BitConverter.ToInt32(MemoryStream.GetBuffer(), 0);
                                    if (this.m_PacketSize > ushort.MaxValue * 16 || this.m_PacketSize < Packet.MinPacketSize)
                                    {
                                        throw new Exception($"recv packet size error, 可能是外网探测端口: {this.m_PacketSize}");
                                    }
                                    break;
                                case Packet.PacketSizeLength2:
                                    this.m_PacketSize = BitConverter.ToUInt16(MemoryStream.GetBuffer(), 0);
                                    if (m_PacketSize > ushort.MaxValue || this.m_PacketSize < Packet.MinPacketSize)
                                    {
                                        throw new Exception($"recv packet size error, 可能是外网探测端口: {this.m_PacketSize}");
                                    }
                                    break;
                                default:
                                    throw new Exception("packet size byte count must be 2 or 4!");
                            }
                            this.m_State = ParserState.PacketBody;
                        }
                        break;
                    }
                    case ParserState.PacketBody:
                    {
                        if (this.m_Buffer.Length < this.m_PacketSize)
                        {
                            finish = true;
                        }
                        else
                        {
                            MemoryStream.Seek(0, SeekOrigin.Begin);
                            MemoryStream.SetLength(this.m_PacketSize);
                            byte[] bytes = MemoryStream.GetBuffer();
                            this.m_Buffer.Read(bytes, 0, m_PacketSize);
                            this.m_IsOK = true;
                            this.m_State = ParserState.PacketSize;
                            finish = true;
                        }
                        break;
                    }
                }
            }
            return this.m_IsOK;
        }

        public MemoryStream GetPacket()
        {
            this.m_IsOK = false;
            return this.MemoryStream;
        }
    }
}